﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using IchiaTester.Utils;
using Newtonsoft.Json;
using static IchiaTester.SerialPortConfigForm;
using static IchiaTester.MainForm;


namespace IchiaTester
{
    

    #region Enum

    public enum POS_ListViewSub
    {
        ID_POS = 0, // No 序号
        NAME_POS, // 测试名称
        TEST_POINT_POS, // 测试点
        LOWER_LIMIT_POS, // 下限
        NORMAL_VALUE_POS, // 标准值
        UPPER_LIMIT_POS, // 上限
        UNIT_POS, // 单位
        TEST_VALUE_POS, // 量测值
        TEST_RESULT_POS // 量测结果
    }
    public enum POS_TestCaseSub
    {
        ID_POS = 0,
        NAME_POS,
        TEST_POINT_POS,
        TEST_TYPE_POS,
        COMMU_TYPE_POS,
        TEST_PARAM_TYPE_POS,
        //
        PORT_POS,
        SLAVE_ID_POS,
        REGISTER_POS,
        VALUE_POS,
        IS_CUSTOM_TX_FRAME_POS,
        CUSTOM_TX_FRAME_POS,
        RX_FRAME_LENGTH_POS,
        CHECK_BYTES_NUMBERS_POS,
        RX_FRAME_HEADER_POS,
        RX_FRAME_TAILER_POS,
        CHECK_BYTE_POS_POS,
        //
        UPPER_LIMIT_POS,
        NORMAL_VALUE_POS,
        LOWER_LIMIT_POS,
        UNIT_POS,
        TIMEOUT_POS,
        DELAY_POS,
        RETRY_MAX_COUNT_POS,
        RETRY_START_CASE_POS,
        RESOLUTION_POS,
        AVG_AMOUNT_POS,
        SYNC_TEST_POS,
    }
    public enum POS_lvTestGroup
    {
        POS_BEFORE_TEST_CASE = 0,
        POS_TEST_CASE,
        POS_AFTER_TEST_CASE
    }
    #endregion

    #region Struct
    public class TestCase
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string TestPoint { get; set; }
        public string TestType { get; set; }
        
        /// <summary>
        /// 传输协议类型
        /// </summary>
        public string CommuType { get; set; }
        public string TestParamType { get; set; }

        public TestCase()
        { }
        public TestCase(int id, string name, string testPoint, string testType, string commuType, string testParamType)
        {
            Id = id;
            Name = name;
            TestPoint = testPoint;
            TestType = testType;
            CommuType = commuType;
            TestParamType = testParamType;
        }
    }

    public class CommuConfig
    {
        /// <summary>
        /// com端口
        /// </summary>
        public int PortID { get; set; }
        
        /// <summary>
        /// 从站地址
        /// </summary>
        public string SlaveID { get; set; }
        
        /// <summary>
        /// 寄存器地址
        /// </summary>
        public string RegAddress { get; set; }
        
        /// <summary>
        /// 寄存器数量
        /// </summary>
        public string NumOfRegister { get; set; }
        public string TxSetting { get; set; }
        public string CustomTxFrame { get; set; }
        public string RxFrameLength { get; set; }
        public string NumOfCheckBytes { get; set; }
        public string RxFrameHeader { get; set; }
        public string RxFrameTailer { get; set; }
        public string PosOfCheckBytes { get; set; }
        public string RxSetting { get; set; }

        public CommuConfig()
        { }

        public CommuConfig(int portID, string slaveID, string registerAddress, string numOfRegister, string txSetting, string customFrame,
            string frameLength, string numOfCheckBytes, string frameHeader, string frameTrailer, string posOfCheckBytes, string rxSetting)
        {
            PortID = portID;
            SlaveID = slaveID;
            RegAddress = registerAddress;
            NumOfRegister = numOfRegister;
            TxSetting = txSetting;
            CustomTxFrame = customFrame;
            RxFrameLength = frameLength;
            NumOfCheckBytes = numOfCheckBytes;
            RxFrameHeader = frameHeader;
            RxFrameTailer = frameTrailer;
            PosOfCheckBytes = posOfCheckBytes;
            RxSetting = rxSetting;
        }
    }

    public class TestParam
    {
        public string UpperLimit { get; set; }
        public string NormalValue { get; set; }
        public string LowerLimit { get; set; }
        public string Unit { get; set; }
        public string Timeout { get; set; }
        public string Delay { get; set; }
        public string RetryMaxCount { get; set; }
        public string RetryStartCase { get; set; }
        public string DataResolution { get; set; }
        public string AvgAmount { get; set; }
        public int CaseChecked { get; set; }
        public string CaseGroup { get; set; }

        public TestParam()
        { }

        public TestParam(string upperLimit, string normalValue, string lowerLimit, 
            string unit, string timeout, string delay, string retryMaxCount, string retryStartCase,
            string dataResolution, string avgAmount, int caseChecked, string caseGroup)
        {
            UpperLimit = upperLimit;
            NormalValue = normalValue;
            LowerLimit = lowerLimit;
            Unit = unit;
            Timeout = timeout;
            Delay = delay;
            RetryMaxCount = retryMaxCount;
            RetryStartCase = retryStartCase;
            DataResolution = dataResolution;
            AvgAmount = avgAmount;
            CaseChecked = caseChecked;
            CaseGroup = caseGroup;
        }
    }
    #endregion
    public class JsonContainer
    {
        public List<TestCase> listCase { get; set; } = new List<TestCase>();
        public List<CommuConfig> listCommu { get; set; } = new List<CommuConfig>();
        public List<TestParam> listParams { get; set; } = new List<TestParam>();
    }
    public class DataManager
    {
        [DllImport("kernel32")]
        private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        #region variables
        //For cases
        public List<TestCase> AllTestCases { get; private set; }
        public List<CommuConfig> AllCommuConfigs { get; private set; }
        public List<TestParam> AllTestParams { get; private set; }
        public List<TestCase> BeforeTestCase { get; private set; }
        public List<TestCase> AfterTestCase { get; private set; }
        public List<CommuConfig> BeforeCommuConfigs { get; private set; }
        public List<CommuConfig> AfterCommuConfigs { get; private set; }
        public List<TestParam> BeforeTestParams { get; private set; }
        public List<TestParam> AfterTestParams { get; private set; }

        /// <summary>
        /// 串口配置项集合
        /// </summary>
        public List<SerialPortConfig> SerialPortConfigs { get; private set; }
        
        /// <summary>
        /// SerialPort集合
        /// </summary>
        public List<SerialPort> serialPorts { get; private set; }
        public TestCase TempTestCase { get; set; }
        public CommuConfig TempCommuConfig { get; set; }
        public TestParam TempTestParam { get; set; }

        List<TestCase> testType = new List<TestCase>();
        List<CommuConfig> commuConfigType = new List<CommuConfig>();
        List<TestParam> testParamType = new List<TestParam>();
        JsonContainer jsonContainer = new JsonContainer();



        private const int CFG_DATA_LENGTH = 28;
        private const int VS_CFG_LENGTH = 9;
        private const int Modbus_Rx_Length = 7;
        private const int Default_Rx_Length = 8;
        public const int FAIL_DELAY_TIME = 500;
        public const int SUCCESS_DELAY_TIME = 500;
        public const int DELEY_TIME_BETWEEN_EACH_FILE = 100;
        public const int DeviceCMD = 4;

        private LoadingItem loadingItem = LoadingItem.iniFile;
        private MainForm mainForm;

        private bool loadingItemFail = false; // 加载文件失败
        public bool EmptySerialPortConfig = false; // 标记串口配置文件中是否为空,默认为空
        public static bool IsPortAvailable = false; // 端口是否可用的标志
        

        public enum LoadingItem
        {
            iniFile = 0,
            TestType,
            TestItem,
            BardcodeLength,
            DisplayInformation,
            UserFile,
            SerialPortConfig,
            OpenSerialPorts,
        }
        public enum ParamType
        {
            FloatNumber = 0,
            LogicLevel,
            CommuCheck,
            AvgCheck,
            DeviceCMD
        }
        public enum CommuType
        {
            Default = 0,
            Modbus,
            DefaultCC,
            ModbusCC
        }

        #endregion

        /// <summary>
        /// 数据管理器
        /// </summary>
        /// <param name="form"></param>
        public DataManager(MainForm form)
        {
            AllTestCases = new List<TestCase>();
            AllCommuConfigs = new List<CommuConfig>();
            AllTestParams = new List<TestParam>();
            BeforeTestCase = new List<TestCase>();
            AfterTestCase = new List<TestCase>();
            BeforeCommuConfigs = new List<CommuConfig>();
            AfterCommuConfigs = new List<CommuConfig>();
            BeforeTestParams = new List<TestParam>();
            AfterTestParams = new List<TestParam>();
            TempTestCase = new TestCase();
            TempCommuConfig = new CommuConfig();
            TempTestParam = new TestParam();
            SerialPortConfigs = new List<SerialPortConfig>();
            mainForm = form;
        }
        
        /// <summary>
        /// 加载数据
        /// </summary>
        public void LoadingData()
        {
            //顯示載入設定過程
            mainForm.UpdateLoadingForm("正在初始化...", false);
            Thread.Sleep(500);
            // 從 INI 檔案中讀取設定值
            SystemSetting.iniFilePath = Path.Combine(Application.StartupPath, SystemSetting.SETTING_FILENAME);

            //預設路徑
            //SystemSetting.testCaseCfgPath = System.Windows.Forms.Application.StartupPath + "\\" + SystemSetting.TEST_CFG_FILENAME;
            SystemSetting.testResultPath = Application.StartupPath + "\\" + SystemSetting.TEST_RESULT_FILENAME;
            SystemSetting.userInfoPath = Application.StartupPath + "\\" + SystemSetting.USER_INFO_FILENAME;
            SystemSetting.serialPortConfigPath = Application.StartupPath + "\\" + SystemSetting.SERIAL_PORT_CONFIG_FILENAME;
            try
            {
                if(File.Exists(SystemSetting.iniFilePath)) // 存在ini格式配置文件
                {
                    // ini配置文件
                    loadingItem = LoadingItem.iniFile;
                    LoadSettingsFromIniFile(SystemSetting.iniFilePath);
                    mainForm.UpdateLoadingForm($"載入系統設置.....OK", false);

                    //載入測試項目檔 
                    loadingItem = LoadingItem.TestItem;

                    LoadTestCases(SystemSetting.testCasePath, AllTestCases, AllCommuConfigs, AllTestParams);   //讀取設定內容到List資料結構
                    LoadTestCases(SystemSetting.beforeTestCasePath, BeforeTestCase, BeforeCommuConfigs, BeforeTestParams);
                    LoadTestCases(SystemSetting.afterTestCasePath, AfterTestCase, AfterCommuConfigs, AfterTestParams);
                    mainForm.Retry_Counter = new int[AllTestCases.Count];
                    RefreshListViewCases();

                    // 没有ini文件,直接将标志改为false,防止报错
                    loadingItemFail = false;
                    if (loadingItemFail)
                    {
                        mainForm.UpdateLoadingForm($"載入測試項目設置: {SystemSetting.testCasePath}.....FAIL", true);
                    }
                    else
                    {
                        mainForm.UpdateLoadingForm($"載入測試項目設置: {SystemSetting.testCasePath}.....OK", false);
                    }
                    
                    
                    Thread.Sleep(DELEY_TIME_BETWEEN_EACH_FILE);

                    /*
                    //barcode長度 
                    try
                    {
                        MainForm.InfoLogAppend("Get barcode length from ini file", MainForm.SystemState.FileLoading);
                        mainForm.barcodeNumber = decimal.Parse(SystemSetting.barcodeLength);
                        mainForm.UpdateLoadingForm($"載入barcode長度設置.....OK", false);
                        MainForm.InfoLogAppend($"Successfully set barcode length to: {mainForm.barcodeNumber}", MainForm.SystemState.LoadFileSuccess);
                    }
                    catch (Exception ex)
                    {
                        //SystemSetting.barcodeLength = "1";
                        mainForm.UpdateLoadingForm($"Barcode 長度未設定.....FAIL", true);
                        loadingItemFail = true;
                        MainForm.ErrorLogAppend("Error! Barcode Length not set", ex.Message, MainForm.SystemState.ErrorHandling);
                    }
                    finally
                    {
                        Thread.Sleep(DELEY_TIME_BETWEEN_EACH_FILE);
                    }
                    */


                    //Load Test Result file 加载测试结果文件
                    loadingItem = LoadingItem.DisplayInformation;
                    LoadCounterInfo(SystemSetting.testResultPath);
                    if (loadingItemFail)
                    {
                        mainForm.UpdateLoadingForm($"載入計數資料設定: {SystemSetting.TEST_RESULT_FILENAME}.....FAIL", true);
                    }
                    else
                    {
                        mainForm.UpdateLoadingForm($"載入計數資料設定: {SystemSetting.TEST_RESULT_FILENAME}.....OK", false);
                    }
                    Thread.Sleep(DELEY_TIME_BETWEEN_EACH_FILE);

                    //load user file 加载用户配置文件
                    loadingItem = LoadingItem.UserFile;
                    loadingItemFail = ! LoadUserFile(SystemSetting.userInfoPath);
                    if (loadingItemFail)
                    {
                        mainForm.UpdateLoadingForm($"載入用戶設定: {SystemSetting.USER_INFO_FILENAME}.....FAIL", true);
                    }    
                    else
                    {
                        mainForm.UpdateLoadingForm($"載入用戶設定: {SystemSetting.USER_INFO_FILENAME}.....OK", false);
                    }
                    Thread.Sleep(DELEY_TIME_BETWEEN_EACH_FILE);

                    //Load Serial Port Config file 加载串口配置文件
                    loadingItem = LoadingItem.SerialPortConfig;
                    loadingItemFail = !(LoadSerialPortConfig(SystemSetting.serialPortConfigPath));
                    if (loadingItemFail)
                    {
                        mainForm.UpdateLoadingForm($"載入串口設定: {SystemSetting.SERIAL_PORT_CONFIG_FILENAME}.....FAIL", true);
                    }
                    else
                    {
                        mainForm.UpdateLoadingForm($"載入串口設定: {SystemSetting.SERIAL_PORT_CONFIG_FILENAME}.....OK", false);
                    }
                    Thread.Sleep(DELEY_TIME_BETWEEN_EACH_FILE);
                    
                    mainForm.DisplayResult();
                    //Display worker information
                    mainForm.DisplayWorkerInfo();

                    //Open com port
                    if (EmptySerialPortConfig)
                    {
                        MessageBox.Show($"串口設定未正確設置，設置完畢請關閉並重啟上位機");
                        WarnLogAppend("Warning! Serial port not open due to empty or wrong config", SystemState.WarningHandling);
                    }
                    else
                    {
                        GetAllSerialPort();
                        if (IsPortAvailable)
                        {
                            mainForm.UpdateLoadingForm($"開啟通訊串口.....OK", false);
                        }
                        else
                        {
                            mainForm.UpdateLoadingForm($"開啟通訊串口.....FAIL", true);
                        }
                    }
                    
                    //Parameter setting
                    mainForm.delayToStart = (int)(decimal.Parse(SystemSetting.delayToStartTest) * 1000);
                    
                }
                else
                {
                    // 不存在ini格式配置文件
                    mainForm.delayToStart = 0;
                    SaveResultXml(mainForm.passNum, mainForm.failNum, mainForm.totalNum, mainForm.yeild, SystemSetting.testResultPath);
                    mainForm.DisplayWorkerInfo();
                    mainForm.DisplayResult();
                    mainForm.UpdateLoadingForm($"系統未設置，設置完畢請關閉並重啟上位機", true);
                    WarnLogAppend("Warning! System not set due to missing ini file!", SystemState.WarningHandling);
                    loadingItemFail = true;
                }
                mainForm.UpdateLoadingForm("載入結束...");
                InfoLogAppend("Data loading finished.", SystemState.LoadFileFinished);

                if (loadingItemFail)
                {
                    Thread.Sleep(FAIL_DELAY_TIME);
                }
                else
                {
                    Thread.Sleep(SUCCESS_DELAY_TIME);
                }
                
                // 载入完成，Close LoadingForm
                mainForm.CloseLoadingForm();

                // notify 唤醒
                if (loadingItemFail)
                {
                    if(IsPortAvailable)
                    {
                        MessageBox.Show($"載入過程發生錯誤，請檢查錯誤項目!", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else
                    {
                        MessageBox.Show($"部分通訊串口不存在，請檢查並重新設定", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    }
                }
                else
                {
                    MessageBox.Show("Loading data finished", "INFO", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
            
            catch (Exception ex)
            {
                // 载入完成，close LoadingForm
                mainForm.CloseLoadingForm();
                //loadingForm.Invoke(new Action(loadingForm.Close));

                //notify
                ErrorLogAppend("Unexpected error occurred during data loading! ", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                MessageBox.Show($"載入過程發生錯誤 : {loadingItem.ToString()}-{ex.Message}", "Init ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #region Test Case
        /// <summary>
        /// 读取设定内容到ListView资料结构
        /// </summary>
        /// <param name="filePath">配置项文件路径</param>
        /// <param name="cases"></param>
        /// <param name="commu"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public bool LoadTestCases(string filePath, List<TestCase> cases, List<CommuConfig> commu, List<TestParam> param)  //Has log
        {
            InfoLogAppend($"{System_msg.LOG_LOADING} {filePath}", SystemState.FileLoading);
            try
            {
                jsonContainer = new JsonContainer();
                cases.Clear();
                commu.Clear();
                param.Clear();
                jsonContainer = FileHandler.LoadFromJson<JsonContainer>(filePath);
                cases.AddRange(jsonContainer.listCase);
                commu.AddRange(jsonContainer.listCommu);
                param.AddRange(jsonContainer.listParams);

                InfoLogAppend($"{System_msg.LOG_LOAD_SUCCESS} {filePath}", SystemState.LoadFileSuccess);
                return true;
            }
            catch (DirectoryNotFoundException ex)
            {
                loadingItemFail = true;
                ErrorLogAppend($"Target directory not found: {filePath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                return false;
            }
            catch (FileNotFoundException ex)
            {
                loadingItemFail = true;
                ErrorLogAppend($"Target file not found: {filePath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                return false;
            }
            catch (JsonReaderException ex)
            {
                loadingItemFail = true;
                ErrorLogAppend($"Failed to load file: {filePath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                return false;
            }
            catch (JsonSerializationException ex)
            {
                loadingItemFail = true;
                ErrorLogAppend($"Failed to load file: {filePath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                return false;
            }
            catch (Exception ex)
            {
                loadingItemFail = true;
                ErrorLogAppend($"{System_msg.LOG_LOAD_FAIL} {filePath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                return false;
            }
            
        }
        
        /// <summary>
        /// 刷新ListView
        /// </summary>
        public void RefreshListViewCases()  //Has log
        {
            InfoLogAppend($"Try to set UI components: ListView Test Cases", SystemState.UISetting);
            mainForm.lvTest.ItemChecked -= mainForm.lvTest_ItemChecked;
            try
            {
                mainForm.lvTest.Items.Clear();

                List<TestCase> testCase = AllTestCases;
                List<TestParam> testParam = AllTestParams;
                List<TestCase> beforeTestCase = BeforeTestCase;
                List<TestCase> afterTestCase = AfterTestCase;
                List<TestParam> beforeTestParam = BeforeTestParams;
                List<TestParam> afterTestParam = AfterTestParams;
                if (beforeTestCase != null && beforeTestParam != null)
                {
                    for (int i = 0; i < beforeTestCase.Count; i++)
                    {
                        if (beforeTestCase[i].TestType == ((int)CaseType.DeviceCMD).ToString() ||
                            beforeTestCase[i].TestType == ((int)CaseType.DeviceCMD_Pass).ToString() ||
                            beforeTestCase[i].TestType == ((int)CaseType.DeviceCMD_Fail).ToString())
                        {
                            continue;
                        }
                        ListViewItem item = new ListViewItem(beforeTestCase[i].Id.ToString());
                        item.SubItems.Add(beforeTestCase[i].Name);
                        item.SubItems.Add(beforeTestCase[i].TestPoint);
                        item.SubItems.Add(beforeTestParam[i].LowerLimit);
                        item.SubItems.Add(beforeTestParam[i].NormalValue);
                        item.SubItems.Add(beforeTestParam[i].UpperLimit);
                        item.SubItems.Add(beforeTestParam[i].Unit);
                        item.SubItems.Add("");
                        item.SubItems.Add("");
                        item.Group = mainForm.lvTest.Groups[(int)POS_lvTestGroup.POS_BEFORE_TEST_CASE];
                        item.Checked = beforeTestParam[i].CaseChecked == 1 ? true : false;
                        mainForm.lvTest.Invoke(new Action(() => { mainForm.lvTest.Items.Add(item); }));
                    }
                }
                if (testCase != null && testParam != null)
                {

                    for (int i = 0; i < testCase.Count; i++)
                    {
                        if (testCase[i].TestType == ((int)CaseType.DeviceCMD).ToString() ||
                            testCase[i].TestType == ((int)CaseType.DeviceCMD_Pass).ToString() ||
                            testCase[i].TestType == ((int)CaseType.DeviceCMD_Fail).ToString())
                        {
                            continue;
                        }
                        ListViewItem item = new ListViewItem(testCase[i].Id.ToString());
                        item.SubItems.Add(testCase[i].Name);
                        item.SubItems.Add(testCase[i].TestPoint);
                        item.SubItems.Add(testParam[i].LowerLimit);
                        item.SubItems.Add(testParam[i].NormalValue);
                        item.SubItems.Add(testParam[i].UpperLimit);
                        item.SubItems.Add(testParam[i].Unit);
                        item.SubItems.Add("");
                        item.SubItems.Add("");
                        item.Group = mainForm.lvTest.Groups[(int)POS_lvTestGroup.POS_TEST_CASE];
                        item.Checked = testParam[i].CaseChecked == 1 ? true : false;
                        mainForm.lvTest.Invoke(new Action(() => { mainForm.lvTest.Items.Add(item); }));
                    }
                }
                if (afterTestCase != null && afterTestParam != null)
                {
                    for (int i = 0; i < afterTestCase.Count; i++)
                    {
                        if (afterTestCase[i].TestType == ((int)CaseType.DeviceCMD).ToString() ||
                            afterTestCase[i].TestType == ((int)CaseType.DeviceCMD_Pass).ToString() ||
                            afterTestCase[i].TestType == ((int)CaseType.DeviceCMD_Fail).ToString())
                        {
                            continue;
                        }
                        ListViewItem item = new ListViewItem(afterTestCase[i].Id.ToString());
                        item.SubItems.Add(afterTestCase[i].Name);
                        item.SubItems.Add(afterTestCase[i].TestPoint);
                        item.SubItems.Add(afterTestParam[i].LowerLimit);
                        item.SubItems.Add(afterTestParam[i].NormalValue);
                        item.SubItems.Add(afterTestParam[i].UpperLimit);
                        item.SubItems.Add(afterTestParam[i].Unit);
                        item.SubItems.Add("");
                        item.SubItems.Add("");
                        item.Group = mainForm.lvTest.Groups[(int)POS_lvTestGroup.POS_AFTER_TEST_CASE];
                        item.Checked = afterTestParam[i].CaseChecked == 1 ? true : false;
                        mainForm.lvTest.Invoke(new Action(() => { mainForm.lvTest.Items.Add(item); }));
                    }
                }
                InfoLogAppend($"Successfully set UI components: ListView items. ", SystemState.UISetSuccess);
            }
            catch (Exception ex)
            {
                //MessageBox.Show("Failed to load file: " + ex.Message);
                ErrorLogAppend($"Failed to set UI components: ListView items. ", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
            }
            finally
            {
                mainForm.lvTest.ItemChecked += mainForm.lvTest_ItemChecked;
            }
        }


        #endregion

        #region SerialPort 
        /// <summary>
        /// 从本地文件加载串口配置
        /// </summary>
        /// <param name="path">预设文件路径</param>
        /// <returns></returns>
        public bool LoadSerialPortConfig(string path)  //Has log
        {
            InfoLogAppend($"{System_msg.LOG_LOADING} {path}", SystemState.FileLoading);
            EmptySerialPortConfig = false;
            try
            {
                string[] lines = File.ReadAllLines(path);
                
                // 内存全部清零
                SerialPortConfigs.Clear();
                SyncStateDict.Clear();
                SyncFrameDict.Clear();
                DefaultBufferSizeDict.Clear();
                
                foreach (var item in lines)
                {

                    string[] parts = item.Split(',');
                    if (parts.Length == CFG_DATA_LEN)
                    {
                        SerialPortConfig config = new SerialPortConfig
                        {
                            PortID = int.Parse(parts[POS_PORT_ID].Trim()),
                            PortName = parts[POS_PORT_NAME].Trim(),
                            BaudRate = int.Parse(parts[POS_BAUD_RATE].Trim()),
                            DataBits = int.Parse(parts[POS_DATA_BITS].Trim()),
                            Parity = parts[POS_PARITY].Trim(),
                            StopBits = parts[POS_STOP_BITS].Trim(),
                            BufferSize = int.Parse(parts[POS_BUFFER_SIZE].Trim()),
                            SyncState = bool.Parse(parts[POS_SYNC_STATE].Trim()),
                            SyncFrame = parts[POS_SYNC_FRAME].Trim(),
                            CRCCheck = parts[POS_CRC_CHECK].Trim(),
                        };
                        SerialPortConfigs.Add(config);
                        SyncStateDict.Add(config.PortName, config.SyncState);
                        SyncFrameDict.Add(config.PortName, config.SyncFrame);
                        DefaultBufferSizeDict.Add(config.PortName, config.BufferSize);
                    }
                }

                EmptySerialPortConfig = false;
                InfoLogAppend($"{System_msg.LOG_LOAD_SUCCESS} {path}", SystemState.LoadFileSuccess);
                return true;
            }
            catch (Exception ex)
            {
                EmptySerialPortConfig = true;
                loadingItemFail = true;
                ErrorLogAppend($"{System_msg.LOG_LOAD_FAIL} {path}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                return false;
            }
        }

        /// <summary>
        /// 获取所有的SerialPort
        /// </summary>
        /// <returns></returns>
        public bool GetAllSerialPort()  //Has log
        {
            try
            {
                InfoLogAppend($"Try to get all serial port.", SystemState.GetValue);
                serialPorts = new List<SerialPort>(SerialPortConfigs.Count);
                for (int i = 0; i < SerialPortConfigs.Count; i++)
                {
                    InfoLogAppend($"Try to setup serial port: {SerialPortConfigs[i].PortName}.", SystemState.SetValue);
                    try
                    {
                        SerialPort serialPort = new SerialPort();
                        serialPort.PortName = SerialPortConfigs[i].PortName;
                        serialPort.BaudRate = SerialPortConfigs[i].BaudRate;
                        serialPort.DataBits = SerialPortConfigs[i].DataBits;
                        serialPort.StopBits = StopBits.One;
                        serialPort.Parity = (Parity)Enum.Parse(typeof(Parity), SerialPortConfigs[i].Parity);
                        serialPort.DataReceived += mainForm.SerialPort_DataReceived;
                        serialPorts.Add(serialPort);
                        InfoLogAppend($"Successfully setup serial port: {SerialPortConfigs[i].PortName}.", SystemState.SetValueSuccess);
                    }
                    catch (Exception ex)
                    {
                        ErrorLogAppend($"Failed to setup serial port: {SerialPortConfigs[i].PortName}.", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                        return false;
                    }
                }
                mainForm.serialBufferReady = new Dictionary<string, bool>();
                spRxBuffer = new Dictionary<string, List<byte>>();
                for (int i = 0; i < serialPorts.Count; i++)
                {
                    if (!mainForm.serialBufferReady.ContainsKey(serialPorts[i].PortName))
                    {
                        mainForm.serialBufferReady.Add(serialPorts[i].PortName, false);
                        spRxBuffer.Add(serialPorts[i].PortName, new List<byte>());
                    }
                }
                InfoLogAppend($"Successfully get all serial port.", SystemState.GetValueSuccess);
                OpenAllSerialPort();
                return true;
            }
            catch (Exception ex)
            {
                loadingItemFail = true;
                ErrorLogAppend($"Failed to get all serial port.", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                return false;
            }
        }

        /// <summary>
        /// 打开所有 com port
        /// </summary>
        public void OpenAllSerialPort()  //Has log
        {
            IsPortAvailable = false;
            foreach (var port in serialPorts)
            {
                InfoLogAppend($"Try to open serial port: {port.PortName}.", SystemState.SetPorts);
                try
                {
                    if (SerialPortHandler.PortIsExist(port.PortName))
                    {
                        if (!port.IsOpen)
                        {
                            port.Open();
                        }
                        else
                        {
                            PauseUpdateVisualizer = true;
                            AllowTestStart = false;
                            Thread.Sleep(500);
                            port.Close();
                            port.Open();
                        }

                        InfoLogAppend($"Successfully open serial port: {port.PortName}.", SystemState.SetPortsSuccess);
                        IsPortAvailable = true;
                        PauseUpdateVisualizer = false;
                        
                    }
                    else
                    {

                        loadingItemFail = true;
                        IsPortAvailable = false;
                        WarnLogAppend($"Warning! Serial port {port.PortName} is not available.", SystemState.WarningHandling);
                    }
                    
                }
                catch (Exception ex)
                {
                    loadingItemFail = true;
                    IsPortAvailable = false;
                    PauseUpdateVisualizer = true;
                    ErrorLogAppend($"Failed to open serial port: {port.PortName}.", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                }
            }
            
        }
        public void RebootSerialPort(SerialPort port)
        {
            InfoLogAppend($"Try to reboot serial port: {port.PortName}.", SystemState.SetPorts);
            try
            {
                port.DiscardInBuffer();
                port.DiscardOutBuffer();
                port.Close();
                Thread.Sleep(100);
                port.Open();
                InfoLogAppend($"Successfully reboot serial port: {port.PortName}.", SystemState.SetPortsSuccess);
            }
            catch (Exception ex)
            {
                
                ErrorLogAppend($"Failed to reboot serial port: {port.PortName}.", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                MessageBox.Show($"Failed to reboot serial port: {port.PortName}.", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
                mainForm.UpdateTbResult(System_msg.CHECK_SERIAL_PORT, Color.Red, string.Empty);

            }
        }
        
        /// <summary>
        /// 关闭所有串口
        /// </summary>
        public void CloseAllSerialPort()  //Has log
        {
            AllowTestStart = false;
            PauseUpdateVisualizer = true;
            Thread.Sleep(100);
            for (int i = 0; i < serialPorts.Count; i++)
            {
                SerialPort port = serialPorts[i];
                try
                {
                    InfoLogAppend($"Try to close serial port: {port.PortName}.", SystemState.SetPorts);
                    if (SerialPortHandler.PortIsExist(port.PortName))
                    {
                        if (port.IsOpen)
                        {
                            port.DiscardInBuffer();
                            port.Close();
                        }
                        InfoLogAppend($"Successfully close serial port: {port.PortName}.", SystemState.SetPortsSuccess);
                    }
                    else
                    {
                        WarnLogAppend($"Warning! Serial port {port.PortName} is not available.", SystemState.WarningHandling);
                        IsPortAvailable = false;
                    }

                }
                catch (Exception ex)
                {
                    ErrorLogAppend($"Failed to close serial port: {port.PortName}.", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                }
            }
        }
        
        #endregion

        #region Types
        /*
        private List<TestType> LoadTestTypeCfgFile(string filePath)
        {
            List<TestType> devices = new List<TestType>();

            string[] lines = File.ReadAllLines(filePath);
            foreach (string line in lines)
            {
                string[] parts = line.Split(',');
                if (parts.Length == 2)
                {
                    int id = int.Parse(parts[0].Trim());
                    string name = parts[1].Trim();
                    devices.Add(new TestType(id, name));
                }
            }
            return devices;
        }

        private void LoadTypes(string filePath, string filename, List<TestType> types)
        {
            if (File.Exists(filePath))
            {
                //Load counter info
                loadingItem = LoadingItem.TestType;
                mainForm.UpdateLoadingForm($"載入測項類別: {filename}.....OK", false);
                types = LoadTestTypeCfgFile(filePath);
            }
            else
            {
                mainForm.UpdateLoadingForm($"載入測項類別: {filename}.....FAIL", true);
                loadingItemFail = true;
            }
        }
        */
        #endregion

        #region INI
        [DllImport("kernel32")]
        private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);

        private string GetPrivateProfileString(string section, string key, string filePath)
        {
            StringBuilder sb = new StringBuilder(255);
            GetPrivateProfileString(section, key, "", sb, 255, filePath);
            return sb.ToString();
        }
        
        /// <summary>
        /// 从ini格式文件加载配置
        /// </summary>
        /// <param name="iniFile">ini文件路径</param>
        public void LoadSettingsFromIniFile(string iniFile)  //Has log
        {
            InfoLogAppend($"{System_msg.LOG_LOADING} {iniFile}", SystemState.FileLoading);
            try
            {
                
                SystemSetting.workId = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.WORKER_ID, iniFile);
                SystemSetting.workShift = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.WORK_SHIFT, iniFile);
                SystemSetting.productId = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.PRODUCT_ID, iniFile);
                SystemSetting.workOrder = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.WORK_ORDER, iniFile);
                SystemSetting.processOrder = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.PROCESS_ORDER, iniFile);
                SystemSetting.boardNum = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.BOARD_NUM, iniFile);
                SystemSetting.productkey = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.PRODUCT_KEY, iniFile);
                SystemSetting.jigId = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.JIG_ID, iniFile);
                SystemSetting.machineId = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.MACHINE_ID, iniFile);
                SystemSetting.appName = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.APP_NAME, iniFile);
                SystemSetting.appVersion = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.APP_VERSION, iniFile);
                SystemSetting.barcodeLength = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.BARCODE_LENGTH, iniFile);
                SystemSetting.delayToStartTest = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.DELAY_TO_TEST, iniFile);
                SystemSetting.delayToWait = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.DELAY_TO_WAIT, iniFile);
                SystemSetting.clearAfterTest = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.CLEAR_AFTER_TEST, iniFile);
                SystemSetting.keepTestCheck = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.KEEP_TEST_CHECK, iniFile);


                //Path
                string ogPath = Path.Combine(SystemSetting.appLogPath, SystemSetting.appLogFilename);
                SystemSetting.testCasePath = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.TEST_CFG_PATH, iniFile);
                SystemSetting.beforeTestCasePath = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.BEFORE_TEST_CASE_PATH, iniFile);
                SystemSetting.afterTestCasePath = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.AFTER_TEST_CASE_PATH, iniFile);
                SystemSetting.factoryLogPath = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.FACTORY_LOG_PATH, iniFile);
                SystemSetting.appLogPath = GetPrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.APP_LOG_PATH, iniFile);
                if (String.IsNullOrEmpty(SystemSetting.appLogPath) || FileHandler.CheckDirectory(SystemSetting.appLogPath))
                {
                    //將路徑指定到起始位置中的applog資料夾
                    SystemSetting.appLogPath = Application.StartupPath + "\\" + "App log";
                    Directory.CreateDirectory(SystemSetting.appLogPath);


                }
                if (String.IsNullOrEmpty(SystemSetting.factoryLogPath) || FileHandler.CheckDirectory(SystemSetting.factoryLogPath))
                {
                    SystemSetting.factoryLogPath = Application.StartupPath + "\\" + "Factory log";
                    Directory.CreateDirectory(SystemSetting.factoryLogPath);

                }
                bool relocate = RelocateAppLog(ogPath);
                if (!relocate)
                {
                    loadingItemFail = false;
                    return;
                }

                //Average Setting
                SystemSetting.averageSetting = GetPrivateProfileString(SystemSetting.INI_SECTION_AVERAGE_SETTING, SystemSetting.AVERAGE_SETTING, iniFile);
                UpdateMenuAverage(SystemSetting.averageSetting);

                //Test Init
                SystemSetting.startButtonCheck = GetPrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.START_BUTTON_CHECK, iniFile);
                SystemSetting.barcodeDetectCheck = GetPrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.BARCODE_DETECT_CHECK, iniFile);
                SystemSetting.deviceCMDCheck = GetPrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.DEVICE_CMD_CHECK, iniFile);
                SystemSetting.askDeviceCheck = GetPrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_CHECK, iniFile);
                SystemSetting.askDeviceCMD = GetPrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_CMD, iniFile);
                SystemSetting.askDevicePort = GetPrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_PORT, iniFile);
                SystemSetting.askDeviceFreq = GetPrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_FREQ, iniFile);
                SystemSetting.askDeviceReturnCMD = GetPrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_RETURN_CMD, iniFile);
                SystemSetting.askDeviceReturnPort = GetPrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_RETURN_PORT, iniFile);

                InfoLogAppend($"{System_msg.LOG_LOAD_SUCCESS} {iniFile}", SystemState.LoadFileSuccess);
            }
            catch (Exception ex)
            {
                loadingItemFail = true;
                ErrorLogAppend($"{System_msg.LOG_LOAD_FAIL} {iniFile}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
            }
           
        }
        
        public void SaveSettingsToIniFile()
        {
            SystemSetting.iniFilePath = Path.Combine(Application.StartupPath, SystemSetting.SETTING_FILENAME);
            InfoLogAppend($"{System_msg.LOG_SAVING} {SystemSetting.iniFilePath}", SystemState.FileSaving);
            try
            {
                // 寫入 INI 檔案

                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.WORKER_ID, SystemSetting.workId, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.WORK_SHIFT, SystemSetting.workShift, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.PRODUCT_ID, SystemSetting.productId, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.WORK_ORDER, SystemSetting.workOrder, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.PROCESS_ORDER, SystemSetting.processOrder, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.BOARD_NUM, SystemSetting.boardNum, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.PRODUCT_KEY, SystemSetting.productkey, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.JIG_ID, SystemSetting.jigId, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.MACHINE_ID, SystemSetting.machineId, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.APP_NAME, SystemSetting.appName, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.APP_VERSION, SystemSetting.appVersion, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.BARCODE_LENGTH, SystemSetting.barcodeLength, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.DELAY_TO_TEST, SystemSetting.delayToStartTest, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.DELAY_TO_WAIT, SystemSetting.delayToWait, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.CLEAR_AFTER_TEST, SystemSetting.clearAfterTest, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.FACTORY_LOG_PATH, SystemSetting.factoryLogPath, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.APP_LOG_PATH, SystemSetting.appLogPath, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, SystemSetting.KEEP_TEST_CHECK, SystemSetting.keepTestCheck, SystemSetting.iniFilePath);


                if (string.IsNullOrEmpty(SystemSetting.averageSetting))
                {
                    SystemSetting.averageSetting = "0";
                }
                WritePrivateProfileString(SystemSetting.INI_SECTION_AVERAGE_SETTING, SystemSetting.AVERAGE_SETTING, SystemSetting.averageSetting, SystemSetting.iniFilePath);

                WritePrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.START_BUTTON_CHECK, SystemSetting.startButtonCheck, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.BARCODE_DETECT_CHECK, SystemSetting.barcodeDetectCheck, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.DEVICE_CMD_CHECK, SystemSetting.deviceCMDCheck, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_CHECK, SystemSetting.askDeviceCheck, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_CMD, SystemSetting.askDeviceCMD, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_PORT, SystemSetting.askDevicePort, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_FREQ, SystemSetting.askDeviceFreq, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_RETURN_CMD, SystemSetting.askDeviceReturnCMD, SystemSetting.iniFilePath);
                WritePrivateProfileString(SystemSetting.INI_SECTION_TEST_INIT_SETTING, SystemSetting.ASK_DEVICE_RETURN_PORT, SystemSetting.askDeviceReturnPort, SystemSetting.iniFilePath);


                InfoLogAppend($"{System_msg.LOG_SAVE_SUCCESS} {SystemSetting.iniFilePath}", SystemState.SaveFileSuccess);
                
            }
            catch (Exception ex)
            {
                ErrorLogAppend($"{System_msg.LOG_SAVE_FAIL} {SystemSetting.iniFilePath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                MessageBox.Show("Fail to save system config! " + $"{Environment.NewLine}" + $"{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

        }
        public void CaseCfgPathHandler(string saveLoc, string path, string iniKey)
        {
            InfoLogAppend($"Update new test case config path in {SystemSetting.iniFilePath}", SystemState.FileSaving);
            saveLoc = path;
            WritePrivateProfileString(SystemSetting.INI_SECTION_SYS_SETTING, iniKey, saveLoc, SystemSetting.iniFilePath);

            InfoLogAppend($"Update new test case config path in {SystemSetting.iniFilePath} success", SystemState.SaveFileSuccess);
        }
        public void UpdateINISetting(string section, string key, string value)
        {
            InfoLogAppend($"Update {key} in {SystemSetting.iniFilePath}", SystemState.FileSaving);
            WritePrivateProfileString(section, key, value, SystemSetting.iniFilePath);
            InfoLogAppend($"Update {key} in {SystemSetting.iniFilePath} success", SystemState.SaveFileSuccess);
        }
        public void UpdateMenuAverage(string value)
        {
            switch (int.Parse(value))
            {
                case (int)AvgType.Default:
                    mainForm.menuDefaultAvg.Checked = true;
                    SystemSetting.AVERAGE_STR = "Default";
                    break;
                case (int)AvgType.Deviation:
                    mainForm.menuDeviation.Checked = true;
                    SystemSetting.AVERAGE_STR = "Deviation";
                    break;
                case (int)AvgType.Median:
                    mainForm.menuMedian.Checked = true;
                    SystemSetting.AVERAGE_STR = "Median";
                    break;
                case (int)AvgType.IQR:
                    mainForm.menuIQR.Checked = true;
                    SystemSetting.AVERAGE_STR = "IQR";
                    break;
                default:

                    break;
            }
        }
        #endregion

        #region User 保存用户信息至xml文件中
        public static void SaveUserFile(string filepath)
        {
            InfoLogAppend($"Try to save user info to: {filepath}", SystemState.FileSaving);
            try
            {
                // 创建FileInfo对象
                FileInfo info = new FileInfo(filepath);
                
                // 将文件类型改为非只读
                if (info.IsReadOnly)
                {
                    info.IsReadOnly = false;
                }
                
                // 创建XmlDocument对象,将字典中用户信息持久化到xml文件中
                XmlDocument doc = new XmlDocument();
                XmlElement rootElement;
                XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0", "utf-8", null);
                // build root element
                rootElement = doc.CreateElement("Users");
                doc.AppendChild(rootElement);
                // 創建多層級元素
                foreach (string name in SystemSetting.UserInfoDict.Keys)
                {
                    XmlElement userInfo = doc.CreateElement(name);

                    XmlElement password = doc.CreateElement("Password");
                    password.InnerText = SystemSetting.UserInfoDict[name].HashPassword;
                    userInfo.AppendChild(password);

                    XmlElement factory = doc.CreateElement("FactoryName");
                    factory.InnerText = SystemSetting.UserInfoDict[name].FactoryName;
                    userInfo.AppendChild(factory);

                    XmlElement permission = doc.CreateElement("Permission");
                    permission.InnerText = SystemSetting.UserInfoDict[name].Permission;
                    userInfo.AppendChild(permission);

                    rootElement.AppendChild(userInfo);  
                }
                doc.Save(filepath);
                info.IsReadOnly = true; // 置为仅可读
                InfoLogAppend($"Successfully save user info to: {filepath}", SystemState.SaveFileSuccess);
            }
            catch (DirectoryNotFoundException ex)
            {
                ErrorLogAppend($"Target directory not found: {filepath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                MessageBox.Show("Target directory not found! " + Environment.NewLine + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (Exception ex)
            {
                ErrorLogAppend($"Unexpected error when save user info to: {filepath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                MessageBox.Show("Unexpected error when save user info! " + Environment.NewLine + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        
        /// <summary>
        /// 加载用户配置文件
        /// </summary>
        /// <param name="filepath">用户配置文件路径</param>
        /// <returns></returns>
        public bool LoadUserFile(string filepath)  //Has log
        {
            InfoLogAppend($"{System_msg.LOG_LOADING} {filepath}", SystemState.FileLoading);
            try
            {
                XmlDocument docRead = new XmlDocument();
                docRead.Load(filepath);

                TraverseXml(docRead.SelectSingleNode("Users"));
                InfoLogAppend($"{System_msg.LOG_LOAD_SUCCESS} {filepath}", SystemState.LoadFileSuccess);
                return true;
            }
            catch (Exception ex)
            {
                loadingItemFail = true;
                ErrorLogAppend($"{System_msg.LOG_LOAD_FAIL} {filepath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                return false;
            }
        }
        
        /// <summary>
        /// 解析xml
        /// </summary>
        /// <param name="node"></param>
        private static void TraverseXml(XmlNode node)
        {
            //SystemSetting.userInfoList = new List<UserInfo>();
            SystemSetting.UserInfoDict = new Dictionary<string, UserInfo>();

            foreach (XmlNode child in node.ChildNodes)
            {
                UserInfo userInfo = new UserInfo();
                string Name = child.Name;
                foreach (XmlNode Att in child.ChildNodes)
                {
                    switch (Att.Name)
                    {
                        case "FactoryName":
                            userInfo.FactoryName = Att.InnerText;
                            break;
                        case "Password":
                            userInfo.HashPassword = Att.InnerText;
                            break;
                        case "Permission":
                            userInfo.Permission = Att.InnerText;
                            break;
                    }
                }
                //Add user info dictionary
                SystemSetting.UserInfoDict.Add(Name, userInfo);
            }
        }
        #endregion

        #region Result
        /// <summary>
        /// 加载测试结果
        /// </summary>
        /// <param name="filepath"></param>
        public void LoadCounterInfo(string filepath)  //Has log
        {
            /*
             testresult.xml 節點
             TestResults   
             |
             +---Pass
             |
             +---Fail
             |
             +---Total
             |
             +---Yield
             */
            InfoLogAppend($"{System_msg.LOG_LOADING} {filepath}", SystemState.FileLoading);
            
            try
            {
                // 读xml文件
                XmlDocument docRead = new XmlDocument();
                docRead.Load(filepath);

                mainForm.passNum = int.Parse(docRead.SelectSingleNode("//Pass").InnerText);
                mainForm.failNum = int.Parse(docRead.SelectSingleNode("//Fail").InnerText);
                mainForm.totalNum = int.Parse(docRead.SelectSingleNode("//Total").InnerText);
                mainForm.yeild = (float)Math.Round(float.Parse(docRead.SelectSingleNode("//Yield").InnerText), 2);
                loadingItemFail = false;
                InfoLogAppend($"{System_msg.LOG_LOAD_SUCCESS} {filepath}", SystemState.LoadFileSuccess);
                
            }
            catch (Exception ex)
            {
                mainForm.passNum = 0;
                mainForm.failNum = 0;
                mainForm.totalNum = 0;
                mainForm.yeild = 0.0f;
                loadingItemFail = true;
                ErrorLogAppend($"{System_msg.LOG_LOAD_FAIL} {filepath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
            }

            
            
        }
        
        /// <summary>
        /// 保存测试结果到xml文件中
        /// </summary>
        /// <param name="pass">通过数</param>
        /// <param name="fail">失败数</param>
        /// <param name="total">总数</param>
        /// <param name="yeild">良率</param>
        /// <param name="filepath">xml文件路径</param>
        public void SaveResultXml(int pass, int fail, int total, float yeild, string filepath)  //Has log
        {
            InfoLogAppend($"Save test result to {filepath}", SystemState.FileSaving);
            try
            {
                // 創建一個新的XML文件
                XmlDocument doc = new XmlDocument();
                XmlElement root = doc.CreateElement("TestResults");
                doc.AppendChild(root);

                // 寫入設定值
                XmlElement passElement = doc.CreateElement("Pass");
                passElement.InnerText = pass.ToString();
                root.AppendChild(passElement);

                XmlElement failElement = doc.CreateElement("Fail");
                failElement.InnerText = fail.ToString();
                root.AppendChild(failElement);

                XmlElement totalElement = doc.CreateElement("Total");
                totalElement.InnerText = total.ToString();
                root.AppendChild(totalElement);

                XmlElement yieldElement = doc.CreateElement("Yield");
                yieldElement.InnerText = yeild.ToString();
                root.AppendChild(yieldElement);

                // 儲存XML文件
                doc.Save(filepath);
                InfoLogAppend($"Save test result to {filepath}", SystemState.SaveFileSuccess);
            }
            catch (Exception ex)
            {
                ErrorLogAppend($"Failed to save test result to {filepath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
            }
        }
        #endregion

        public bool RelocateAppLog(string ogPath)  //Has log
        {
            //原本log寫在預設路徑，讀取ini後更換到新的路徑
            string newPath = Path.Combine(SystemSetting.appLogPath, SystemSetting.appLogFilename);
            try
            {
                if (!File.Exists(newPath))
                {
                    string content = File.ReadAllText(ogPath);
                    File.WriteAllText(newPath, content);
                    File.Delete(ogPath);
                    InfoLogAppend($"Move log file from: {ogPath} to: {newPath}", SystemState.SaveFileSuccess);
                }
                else
                {
                    WarnLogAppend($"Log file already exists in {newPath}", SystemState.WarningHandling);
                }
                return true;
            }
            catch (DirectoryNotFoundException)
            {
                SystemSetting.appLogPath = Path.GetDirectoryName(ogPath);
                WarnLogAppend($"Log path not found, use previous path: {ogPath}", SystemState.WarningHandling);
                return false;
            }
            catch (Exception ex)
            {
                SystemSetting.appLogPath = Path.GetDirectoryName(ogPath);
                ErrorLogAppend($"Failed to move log file from {ogPath} to {newPath}", ex.Message + ex.StackTrace, SystemState.ErrorHandling);
                return false;
            }
            
        }

    }

}
